# -*- coding: utf-8 -*-
# @version : 1.0
# @File    : auth.py
# @Date    : 2024/9/13 18:10
# @Author  : GlenZhao
# @Desc    : 用户凭证验证装饰器

import jwt
from fastapi import Request, WebSocket
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession
from datetime import timedelta, datetime
from starlette.datastructures import Headers

from apps.auth.user.models import AuthUser
from apps.auth.user.schemas import UserCache
from utils import status
from core.exception import CustomException
from application import settings, constant


class Auth(BaseModel):
    db: AsyncSession
    user: AuthUser | None
    user_cache: UserCache | None
    headers: Headers | None
    token: str | None

    class Config:
        # 接收任意类型
        arbitrary_types_allowed = True

    @property
    def user_id(self):
        if self.user:
            return self.user.id
        if self.user_cache:
            return self.user_cache.id
        raise CustomException(**constant.ErrorsEnum.USER_NOT_EXIST.to_dict())


class WebsocketAuth(BaseModel):
    websocket: WebSocket
    db: AsyncSession
    user: AuthUser

    class Config:
        # 接收任意类型
        arbitrary_types_allowed = True


class AuthValidation:
    """
    用于用户每次调用接口时，验证用户提交的token是否正确，并从token中获取用户信息
    """

    # status_code = 401 时，表示强制要求重新登录，因账号已冻结，账号已过期，手机号码错误，刷新token无效等问题导致
    # 只有 code = 401 时，表示 token 过期，要求刷新 token
    # 只有 code = 错误值时，只是报错，不重新登陆
    error_code = status.HTTP_401_UNAUTHORIZED
    warning_code = status.HTTP_ERROR

    # status_code = 403 时，表示强制要求重新登录，因无系统权限，而进入到系统访问等问题导致

    @staticmethod
    def forbidden(msg: str):
        raise CustomException(
            msg=msg,
            code=status.HTTP_403_FORBIDDEN,
            status_code=status.HTTP_403_FORBIDDEN
        )

    @staticmethod
    def no_permission():
        raise CustomException(
            **constant.ErrorsEnum.NO_PERMISSION.to_dict()
        )

    @classmethod
    def validate_token(cls, request: Request | None, token: str | None) -> int:
        """
        验证用户 token
        """
        if not token:
            AuthValidation.forbidden("请您先登录！")
        try:
            payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])

            is_refresh: bool = payload.get("is_refresh")
            id: int = payload.get('id')
            if is_refresh or not id:
                AuthValidation.forbidden("未认证，请您重新登录")

            if request:
                exp: int = payload.get("exp")
                # 计算当前时间 + 缓冲时间是否大于等于 JWT 过期时间
                buffer_time = (datetime.now() + timedelta(minutes=settings.ACCESS_TOKEN_CACHE_MINUTES)).timestamp()
                # print("过期时间", exp, datetime.fromtimestamp(exp))
                # print("当前时间", buffer_time, datetime.fromtimestamp(buffer_time))
                # print("剩余时间", exp - buffer_time)
                if buffer_time >= exp:
                    request.scope["if-refresh"] = 1
                else:
                    request.scope["if-refresh"] = 0

            return id
        except (jwt.exceptions.InvalidSignatureError, jwt.exceptions.DecodeError):
            AuthValidation.forbidden("无效认证，请您重新登录")
        except jwt.exceptions.ExpiredSignatureError:
            raise CustomException(msg="认证已失效，请您重新登录", code=cls.error_code, status_code=cls.error_code)

    @classmethod
    async def validate_user(cls, request: Request, user_cache: UserCache, user: AuthUser | None, db: AsyncSession, token: str | None) -> Auth:
        """
        验证用户信息
        :param request:
        :param user_cache:
        :param user:
        :param db:
        :return:
        """
        if not user:
            if user_cache is None:
                raise CustomException(msg="未认证，请您重新登陆", code=cls.error_code, status_code=cls.error_code)
        try:
            request.scope["body"] = await request.body()
        except RuntimeError:
            request.scope["body"] = "获取失败"

        return Auth(user=user, user_cache=user_cache, db=db, headers=request.headers, token=token)

    @classmethod
    async def get_user_permissions(cls, user: AuthUser):
        if user.is_admin():
            return {'*'}
        permissions = set()
        # TODO 添加权限模块
        return permissions

    @classmethod
    async def get_user_data_range(cls, user: AuthUser, db: AsyncSession) -> tuple:
        """
        获取用户数据范围
        0 仅本人数据权限
        1
        2
        3 自定义数据权限
        4 全部数据权限
        :param user:
        :param db:
        :return:
        """
        if user.is_admin():
            return 4, ["*"]
        data_range = max([i.data_range for i in user.roles])

        group_ids = set()
        if data_range == 0:
            pass
        elif data_range == 1:
            pass
        elif data_range == 2:
            pass
        elif data_range == 3:
            pass
        elif data_range == 4:
            group_ids.add("*")
        return data_range, list(group_ids)